﻿/* SharpZipHelper
 * 需要引用：ICSharpCode.SharpZipLib.dll
 */

/* 未完事项
 * 1、单元测试。
 */
namespace LSLib.Compress
{
    using ICSharpCode.SharpZipLib.Checksums;
    using ICSharpCode.SharpZipLib.Zip;
    using System;
    using System.IO;

    public static class SharpZipHelper
    {
        /// <summary>FastZip压缩
        /// </summary> 
        /// <param name="sourceDirectory">待压缩的文件夹(包含物理路径)</param>
        /// <param name="zipFileName">压缩后的文件名(包含物理路径)</param>
        public static void FastZip_Compress(string sourceDirectory, string zipFileName)
        {
            FastZip fz = new FastZip();
            fz.CreateEmptyDirectories = true;
            fz.CreateZip(zipFileName, sourceDirectory, true, string.Empty);
            fz = null;
        }

        /// <summary>Zip压缩
        /// </summary>
        /// <param name="sourceDirectory">待压缩的文件夹(包含物理路径)</param>
        /// <param name="zipFileName">压缩后的文件名(包含物理路径)</param>
        /// <param name="level">6</param>
        public static bool Zip_Compress(string sourceDirectory, string zipFileName, int level)
        {
            if (Directory.Exists(sourceDirectory))
                return ZipDictory(sourceDirectory, zipFileName, level);
            else if (File.Exists(sourceDirectory))
                return ZipFile(sourceDirectory, zipFileName, level);
            else
                return false;
        }

        /// <summary>解压缩
        /// </summary>
        /// <param name="zipFileName">待解压文件名(包含物理路径)</param>
        /// <param name="targetDirectory">解压到哪个目录中(包含物理路径)</param>
        public static bool Zip_Decompress(string zipFileName, string targetDirectory)
        {
            if (string.IsNullOrEmpty(zipFileName)) throw new ArgumentNullException("zipFileName");
            if (File.Exists(zipFileName) == false) throw new FileNotFoundException("待解压的文件不存在!", zipFileName);

            if (Directory.Exists(targetDirectory) == false) Directory.CreateDirectory(targetDirectory);

            ZipEntry theEntry = null;
            string dirName = string.Empty;
            string fileName = string.Empty;
            using (ZipInputStream zipStream = new ZipInputStream(File.OpenRead(zipFileName)))
            {
                while ((theEntry = zipStream.GetNextEntry()) != null)
                {
                    dirName = Path.GetDirectoryName(theEntry.Name);
                    if (dirName != string.Empty) Directory.CreateDirectory(Path.Combine(targetDirectory, dirName));

                    fileName = Path.GetFileName(theEntry.Name);
                    if (fileName != string.Empty)
                    {
                        using (FileStream streamWriter = File.Create(Path.Combine(targetDirectory, theEntry.Name)))
                        {
                            int size = 2048;
                            byte[] data = new byte[2048];
                            while (true)
                            {
                                size = zipStream.Read(data, 0, data.Length);
                                if (size <= 0) break;
                                streamWriter.Write(data, 0, size);
                            }
                        }
                    }
                }
            }
            return true;
        }

        #region 私有方法
        /// <summary>压缩单个文件
        /// </summary>
        /// <param name="sourceFile">待压缩的文件名(包含物理路径)</param>
        /// <param name="zipFileName">压缩后的文件名(包含物理路径)</param>
        private static bool ZipFile(string sourceFile, string zipFileName, int level)
        {
            if (File.Exists(sourceFile) == false) throw new FileNotFoundException("待压缩的文件不存在!", sourceFile);

            bool result = true;
            try
            {
                byte[] buffer;
                using (FileStream fsOpenRead = File.OpenRead(sourceFile))
                {
                    buffer = new byte[fsOpenRead.Length];
                    fsOpenRead.Read(buffer, 0, buffer.Length);
                }

                using (ZipOutputStream zipStream = new ZipOutputStream(File.Create(zipFileName)))
                {
                    zipStream.PutNextEntry(new ZipEntry(Path.GetFileName(sourceFile)));
                    zipStream.SetLevel(level);
                    zipStream.Write(buffer, 0, buffer.Length);
                }
            }
            catch { result = false; }
            finally
            {
                GC.Collect();
                GC.Collect(1);
            }
            return result;
        }

        /// <summary>压缩目录
        /// </summary>
        /// <param name="sourceDirectory">待压缩的文件夹(包含物理路径)</param>
        /// <param name="zipFileName">压缩后的文件名(包含物理路径)</param>
        private static bool ZipDictory(string sourceDirectory, string zipFileName, int level)
        {
            if (Directory.Exists(sourceDirectory) == false)
                throw new DirectoryNotFoundException(string.Format("待压缩的文件夹不存在！/r/n{0}", sourceDirectory));

            bool result = false;
            using (ZipOutputStream zipStream = new ZipOutputStream(File.Create(zipFileName)))
            {
                zipStream.SetLevel(level);
                result = ZipDictory(sourceDirectory, zipStream, string.Empty);
                zipStream.Finish();
            }
            return result;
        }
        /// <summary>递归压缩文件夹方法
        /// </summary>
        /// <param name="sourceDirectory">待压缩的文件夹(包含物理路径)</param>
        /// <param name="zipStream">ZipOutputStream</param>
        /// <param name="parentDirName"></param>
        private static bool ZipDictory(string sourceDirectory, ZipOutputStream zipStream, string parentDirName)
        {
            bool result = true;
            Crc32 crc = new Crc32();

            try
            {
                ZipEntry entry = new ZipEntry(Path.Combine(parentDirName, Path.GetFileName(sourceDirectory)/* + "/" */));
                zipStream.PutNextEntry(entry);
                zipStream.Flush();
                string[] filenames = Directory.GetFiles(sourceDirectory);
                foreach (string file in filenames)
                {
                    byte[] buffer;
                    using (FileStream fs = File.OpenRead(file))
                    {
                        buffer = new byte[fs.Length];
                        fs.Read(buffer, 0, buffer.Length);
                    }

                    entry = new ZipEntry(Path.Combine(parentDirName, Path.GetFileName(sourceDirectory), Path.GetFileName(file)));
                    entry.DateTime = System.DateTime.Now;
                    entry.Size = buffer.Length;

                    crc.Reset();
                    crc.Update(buffer);
                    entry.Crc = crc.Value;

                    zipStream.PutNextEntry(entry);
                    zipStream.Write(buffer, 0, buffer.Length);
                }
            }
            catch { result = false; }
            finally
            {
                GC.Collect();
                GC.Collect(1);
            }

            string[] folders = Directory.GetDirectories(sourceDirectory);
            foreach (string folder in folders)
            {
                string path = Path.Combine(parentDirName, Path.GetFileName(sourceDirectory));
                if (ZipDictory(folder, zipStream, path) == false) return false;
            }
            return result;
        }
        #endregion
    }

}
